<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>D3 chart</title>
    <style>
        .chart {
            display: block;
            margin: 0 auto;
        }
        .axis path, .axis line, .axis--y text {
            stroke: #fff;
        }
        .axis--x text {
            stroke: #333;
        }
    </style>
    <script src="{{ url_for('static', filename = 'js/libs/lodash.core.min.js', _external = True) }}"></script>
    <script src="{{ url_for('static', filename = 'js/libs/d3.min.js', _external = True) }}"></script>
</head>
<body>
<svg id="chartView"></svg>
<script>
    (function () {
        var options = {
            title: {
                text: 'F30 Rework Time (All technologies F0 to F2)'
            },
            width: 1000,
            height: 420,
            boxMarginTop: 40,
            boxMarginLeft: 68,
            boxWidth: 930,
            boxHeight: 290,
            graphMarginTop: 10,
            graphMarginLeft: 30,
            axisHeight: 225,
            grid: [
                {
                    show: true,
                    tx: 0,
                    ty: 0,
                    width: '105'
                },
                {
                    show: true,
                    tx: 135,
                    ty: 0,
                    width: '360'
                },
                {
                    show: true,
                    tx: 525,
                    ty: 0,
                    width: '120'
                },
                {
                    show: true,
                    tx: 675,
                    ty: 0,
                    width: '210'
                }
            ],
            xAxis: [{
                textClass: 'r90',
                gridIndex: 0,
                z: 2,
                splitLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        width: 1
                    }
                },
                axisLabel: {
                    "interval": 0,
                    "rotate": 90,
                    "show": true,
                    "textStyle": {
                        "fontSize": 12
                    }
                },
                data: ["2014", "2015", "2016"]
            }, {
                textClass: 'r90',
                gridIndex: 1,
                z: 2,
                splitLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        width: 1
                    }
                },
                data: ["10", "11", "12", "01", "02", "03", "04", "05", "06", "07", "08", "09"]
            }, {
                textClass: 'r90',
                gridIndex: 2,
                z: 2,
                splitLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        width: 1
                    }
                },
                data: ["35", "36", "37", "38"]
            }, {
                textClass: 'r90',
                gridIndex: 3,
                z: 2,
                splitLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        width: 1
                    }
                },
                axisLabel: {
                    "interval": 0,
                    "rotate": 90,
                    "show": true,
                    "textStyle": {
                        "fontSize": 12
                    }
                },
                data: ["19-SEP", "20-SEP", "21-SEP", "22-SEP", "23-SEP", "24-SEP", "25-SEP"]
            }],
            yAxis: [{
                gridIndex: 0,
                z: 2,
                axisTick: {
                    show: true
                },
                axisLine: {
                    show: true
                },
                splitLine: {
                    lineStyle: {
                        color: '#E8ECF1',
                        width: 1
                    }
                },
                min: 0,
                max: 24
            }, {
                gridIndex: 1,
                z: 2,
                axisTick: {
                    show: false
                },
                axisLine: {
                    show: false
                },
                splitLine: {
                    lineStyle: {
                        color: '#E8ECF1',
                        width: 1
                    }
                },
                axisLabel: {
                    "show": false
                },
                min: 0,
                max: 24
            }, {
                gridIndex: 2,
                z: 2,
                axisTick: {
                    show: false
                },
                axisLine: {
                    show: false
                },
                splitLine: {
                    lineStyle: {
                        color: '#E8ECF1',
                        width: 1
                    }
                },
                axisLabel: {
                    "show": false
                },
                min: 0,
                max: 24
            }, {
                gridIndex: 3,
                z: 2,
                axisTick: {
                    show: false
                },
                axisLine: {
                    show: false
                },
                splitLine: {
                    lineStyle: {
                        color: '#E8ECF1',
                        width: 1
                    }
                },
                axisLabel: {
                    "show": false
                },
                min: 0,
                max: 24
            }],
            series: [{
                name: 'Year',
                type: 'bar',
                xAxisIndex: 0,
                yAxisIndex: 0,
                itemStyle: {
                    color: '#00CA66'
                },
                stack: 1,
                barCategoryGap: '1',
                rowNum: 3,
                data: [15, 15, 6]
            }, {
                name: 'Year',
                type: 'bar',
                xAxisIndex: 0,
                yAxisIndex: 0,
                itemStyle: {
                    color: '#FFFD8E'
                },
                stack: 1,
                barCategoryGap: '1',
                data: [3]
            }, {
                name: 'Year',
                type: 'bar',
                xAxisIndex: 0,
                yAxisIndex: 0,
                itemStyle: {
                    color: '#FF7F80'
                },
                stack: 1,
                barCategoryGap: '1',
                data: [6, 9, 18]
            }, {
                name: 'Year',
                type: 'line',
                xAxisIndex: 0,
                yAxisIndex: 0,
                itemStyle: {
                    color: '#333'
                },
                stack: 101,
                rowNum: 1,
                data: [9.64, 4.66, 3.19]
            }, {
                name: 'Year',
                type: 'line',
                xAxisIndex: 0,
                yAxisIndex: 0,
                itemStyle: {
                    color: '#94ddff'
                },
                stack: 102,
                rowNum: 2,
                data: [8.53, 3.84, 2.65]
            }, {
                name: 'Month',
                type: 'bar',
                xAxisIndex: 1,
                yAxisIndex: 1,
                itemStyle: {
                    color: '#00CA66'
                },
                stack: 2,
                rowNum: 3,
                barCategoryGap: '1',
                data: [15, 15, 15, 6, 6, 6, 6, 6, 6, 6, 6, 6]
            }, {
                name: 'Month',
                type: 'bar',
                xAxisIndex: 1,
                yAxisIndex: 1,
                itemStyle: {
                    color: '#FF7F80'
                },
                stack: 2,
                barCategoryGap: '1',
                data: [9, 9, 9, 18, 18, 18, 18, 18, 18, 18, 18, 18]
            }, {
                name: 'Month',
                type: 'line',
                xAxisIndex: 1,
                yAxisIndex: 1,
                itemStyle: {
                    color: '#94ddff'
                },
                stack: 102,
                rowNum: 1,
                data: [3.07, 2.17, 2.30, 2.06, 2.09, 2.96, 4.12, 1.69, 2.85, 2.59, 2.55, 2.9]
            }, {
                name: 'Month',
                type: 'line',
                xAxisIndex: 1,
                yAxisIndex: 1,
                itemStyle: {
                    color: '#333'
                },
                stack: 102,
                rowNum: 2,
                data: [3.83, 2.67, 2.74, 2.65, 2.90, 3.53, 4.59, 2, 3.19, 2.94, 2.96, 3.92]
            }, {
                name: 'Week',
                type: 'bar',
                xAxisIndex: 2,
                yAxisIndex: 2,
                itemStyle: {
                    color: '#00CA66'
                },
                stack: 3,
                rowNum: 3,
                barCategoryGap: '1',
                data: [6, 6, 6, 6]
            }, {
                name: 'Week',
                type: 'bar',
                xAxisIndex: 2,
                yAxisIndex: 2,
                itemStyle: {
                    color: '#FF7F80'
                },
                stack: 3,
                barCategoryGap: '1',
                data: [18, 18, 18, 18]
            }, {
                name: 'Week',
                type: 'line',
                xAxisIndex: 2,
                yAxisIndex: 2,
                itemStyle: {
                    color: '#333'
                },
                stack: 103,
                rowNum: 1,
                data: [2.22, 5.80, 2.49]
            }, {
                name: 'Week',
                type: 'line',
                xAxisIndex: 2,
                yAxisIndex: 2,
                itemStyle: {
                    color: '#94ddff'
                },
                stack: 103,
                rowNum: 2,
                data: [1.61, 4.23, 1.96]
            }, {
                name: 'Day',
                type: 'bar',
                xAxisIndex: 3,
                yAxisIndex: 3,
                itemStyle: {
                    color: '#00CA66'
                },
                stack: 4,
                rowNum: 3,
                barCategoryGap: '1',
                data: [6, 6, 6, 6, 6, 6, 6]
            }, {
                name: 'Day',
                type: 'bar',
                xAxisIndex: 3,
                yAxisIndex: 3,
                itemStyle: {
                    color: '#FF7F80'
                },
                stack: 4,
                barCategoryGap: '1',
                data: [18, 18, 18, 18, 18, 18, 18]
            }]
        };


        var DChart = function (selector, opt) {
            this.selector = selector;
            this.options = opt;
            this.items = {}; // 缓存图片中的组件
            this.init();
            return this;
        };

        DChart.prototype.init = function () {
            this.chart = this.getCharView();
            this.setTitle();
            this.addImage();
            this.drawChart();
        };

        DChart.prototype.setTitle = function () {
            var chartView = this.chart;
            chartView
                .append('text')
                .attr('x', 68)
                .attr('y', 30)
                .text('F30 Rework Time (All technologies F0 to F2)')
                .attr('font-weight', 'bold')
                .attr('font-size', '16')
                .attr('fill', '#999')
                .style('font-weight', 'bold')
                .style('font-family', '"Arial","Microsoft YaHei"');

            chartView.append('g')
                .attr('transform', 'translate(0, 412)')
                .append('text')
                .text('Quality Department BBT-T-40')
                .attr('fill', '#333')
                .attr('font-size', '12')
                .style('font-weight', 'bold')
                .style('transform', 'scale(.8)')
                .style('text-anchor', 'start')
                .style('font-family', '"Arial","Microsoft YaHei"');
        };

        DChart.prototype.addImage = function () {
            var chartView = this.chart,
                opt = this.options;
            var logoOpt = {
                imgWidth: 274,
                imgHeight: 80,
                height: 40,
                y: 0
            };

            var zoomScale = parseFloat(logoOpt['height'] / logoOpt['imgHeight']).toFixed(2);
            logoOpt['width'] = logoOpt['imgWidth'] * zoomScale;
            logoOpt['x'] = opt['width'] - logoOpt['width'];

            // add logo
            chartView
                .append('image')
                .attr("xlink:href", "{{ url_for('static', filename = 'images/bmw_group_logo.jpg', _external = True) }}")
                .attr('width', 274)
                .attr('height', 80)
                .attr('transform', 'translate(' + logoOpt['x'] + ', ' + logoOpt['y'] + ') scale(' + zoomScale + ')');
        };

        DChart.prototype.drawChart = function () {
            var me = this,
                opt = this.options,
                gridData = opt.grid,
                items = this.items,
                chartView = this.chart,
                chartBox = items['chartBox'] = chartView
                    .append('g')
                    .attr("transform", "translate(" + opt['boxMarginLeft'] + "," + opt['boxMarginTop'] + ")");

            chartBox.append('rect')
                .attr('width', opt['boxWidth'])
                .attr('height', opt['boxHeight'])
                .style('fill', '#999');

            items['chartGridLabel'] = chartBox
                .append('g')
                .attr("transform", "translate(" + opt['graphMarginLeft'] + "," + opt['graphMarginTop'] + ")");

            items['grids'] = []; // 缓存grid

            me.drawTableHead();
            _.forEach(gridData, function (dv, dk) {
                me.drawAxis(dv, dk);
                me.drawTable(dv, dk);
            });
            me.drawGraph();
        };

        DChart.prototype.drawAxis = function (obj, idx) {
            var opt = this.options,
                items = this.items,
                xItems = opt.xAxis,
                yItems = opt.yAxis,
                chartGridLabel = items['chartGridLabel'];
            var gridGroup = chartGridLabel.append('g')
                .attr('transform', 'translate(' + obj['tx'] + ',' + obj['ty'] + ')');
            items['grids'].push(gridGroup);

            var xItem = xItems[idx],
                yItem = yItems[idx],
                gridWidth = obj['width'],
                xData = xItem['data'];

            var x = d3.scaleBand()
                .range([0, gridWidth])
                .domain(xData);

            var y = items['yScale'] = d3.scaleLinear()
                .domain([0, 24])
                .range([opt['axisHeight'], 0]);

            var xAxis = d3.axisBottom(x);
            var xGroup = gridGroup.append('g')
                .attr('class', 'axis axis--x')
                .attr('transform', 'translate(0,' + opt['axisHeight'] + ')')
                .call(xAxis);
            if (xItem['textClass']) {
                xGroup.selectAll('text')
                    .attr('x', -10)
                    .attr('y', -2)
                    .attr('transform', 'rotate(-90)')
                    .attr('text-anchor', 'end');
            }

            if (!(yItem['axisLine'] && yItem['axisLine']['show'] === false)) {
                var yAxis = d3.axisLeft(y).tickValues(y.ticks(3).concat([24]));
                gridGroup.append('g')
                    .attr('class', 'axis axis--y')
                    .call(yAxis);
            }
        };

        DChart.prototype.drawGraph = function () {
            var opt = this.options,
                items = this.items,
                series = opt.series,
                xItems = opt.xAxis,
                gridItems = opt.grid,
                chartItems = items['grids'];

            var barAccumer = {}; // bar 累加器
            var yScaleFun = items['yScale'],
                rowHeight = 20;
            _.forEach(series, function (obj, idx) {
                var xIdx = obj['xAxisIndex'],
                    xItem = xItems[xIdx],
                    gridItem = gridItems[xIdx],
                    xData = xItem['data'],
                    xScaleLen = xData.length,
                    chartItem = chartItems[xIdx],
                    barWidth = gridItem['width'] / xScaleLen,
                    tableGroup = items['tableGroup'][xIdx];
                if (obj['type'] == 'bar') {
                    var tmpArr = barAccumer[obj.name];
                    if (!tmpArr) {
                        var n = 0,
                            tmpArr = [];
                        for (n; n < xScaleLen; n++) {
                            tmpArr.push(0);
                        }
                    }
                    _.forEach(obj.data, function (ov, ok) {
                        tmpArr[ok] = parseInt(tmpArr[ok]) + parseInt(ov);
                        if (obj.rowNum) {
                            if (ov < 10) {
                                ov = parseFloat(ov).toFixed(2);
                            }
                            tableGroup.append('text')
                                .attr('x', ok * barWidth + (barWidth / 2))
                                .attr('y', 3 * rowHeight - 8)
                                .text(ov)
                                .attr('alignment-baseline', 'middle')
                                .attr('font-size', '10px')
                                .attr('fill', '#333')
                                .style('text-anchor', 'middle')
                                .style('font-weight', 'bold')
                                .style('font-family', '"Arial","Microsoft YaHei"');
                        }
                    });

                    barAccumer[obj.name] = tmpArr;

                    chartItem.selectAll('.bar.bar' + idx)
                        .data(obj['data'])
                        .enter()
                        .append('rect')
                        .attr('class', 'bar bar' + idx)
                        .attr('width', barWidth)
                        .attr('x', function (d, i) {
                            return i * barWidth;
                        })
                        .attr('y', function (d, i) {
//                             // y轴坐标起始值
                            var yLen = barAccumer[obj.name][i];
                            return yScaleFun(yLen);
                        })
                        .attr('height', function (d) {
                            return yScaleFun(24 - d);
                        })
                        .attr('fill', obj['itemStyle']['color'])
                        .attr('stroke', '#ffffff')
                        .attr('stroke-width', 1);
                }
                else if (obj['type'] == 'line') {

                    _.forEach(obj.data, function (ov, ok) {
                        if (obj.rowNum) {
                            if (ov < 10) {
                                ov = parseFloat(ov).toFixed(2);
                            }
                            tableGroup.append('text')
                                .attr('x', ok * barWidth + (barWidth / 2))
                                .attr('y', obj.rowNum * rowHeight - 8)
                                .text(ov)
                                .attr('alignment-baseline', 'middle')
                                .attr('font-size', '10px')
                                .attr('fill', '#333')
                                .style('text-anchor', 'middle')
                                .style('font-weight', 'bold')
                                .style('font-family', '"Arial","Microsoft YaHei"');
                        }
                    });

                    var lineFun = d3.line().x(function (d, i) {
                        return i * barWidth + barWidth / 2;
                    }).y(function (d) {
                        return yScaleFun(d);
                    });

                    var linePath = chartItem.append('path');

                    linePath.datum(obj['data'])
                        .attr('class', 'line')
                        .attr('d', lineFun)
                        .attr('stroke-width', 2);
                    var totalLength = linePath.node().getTotalLength();

                    linePath.attr("stroke-dasharray", totalLength + " " + totalLength)
                        .attr("stroke-dashoffset", totalLength)
                        .transition()
                        .ease(d3.easeLinear)
                        .duration(1000)
                        .attr("stroke-dashoffset", 0)
                        .attr('fill', 'none')
                        .attr('stroke', obj['itemStyle']['color']);

                    linePath.on('mouseover', function () {
                        var $me = d3.select(this),
                            defStroke = $me.attr('stroke');
                        $me.attr('def-stroke', defStroke);
                        $me.attr('stroke', 'yellow');
                        $me.attr('stroke-width', 3);
                    }).on('mouseout', function () {
                        var $me = d3.select(this),
                            defStroke = $me.attr('def-stroke');
                        $me.attr('stroke', defStroke);
                        $me.attr('stroke-width', 2);
                    });

                    var linePoint = chartItem.selectAll('.circle.circle' + idx)
                        .data(obj['data'])
                        .enter()
                        .append('circle');
                    linePoint.attr('class', 'circle circle' + idx)
                        .attr('cx', function (d, i) {
                            return i * barWidth + barWidth / 2;
                        })
                        .attr('cy', function (d) {
                            return yScaleFun(d);
                        })
                        .attr('r', 3)
                        .attr('stroke', obj['itemStyle']['color'])
                        .attr('stroke-width', 1)
                        .attr('fill', '#fff');

                    linePoint.on('mouseover', function () {
                        var $me = d3.select(this),
                            defFill = $me.attr('fill'),
                            defR = $me.attr('r');
                        $me.attr('def-fill', defFill);
                        $me.attr('fill', 'yellow');
                        $me.attr('def-r', defR);
                        $me.attr('r', 5);
                    }).on('mouseout', function () {
                        var $me = d3.select(this),
                            defFill = $me.attr('def-fill'),
                            defR = $me.attr('def-r');
                        $me.attr('fill', defFill);
                        $me.attr('r', defR);
                    });
                }
            });
        };

        DChart.prototype.getCharView = function () {
            var opt = this.options,
                chartView = d3.select(this.selector)
                    .attr("class", "chart")
                    .attr('width', opt.width)
                    .attr('height', opt.height);
            return chartView;
        };

        DChart.prototype.drawTableHead = function () {
            var opt = this.options,
                items = this.items,
                chartBox = items['chartBox'],
                rowHeight = 20,
                m = 0,
                rowNum = 3,
                offtop = opt['boxHeight'] + 10,
                tdArr = [{
                    text: 'Total',
                    color: '#bebebe'
                }, {
                    text: 'Offline',
                    color: '#bebebe'
                }, {
                    text: 'Target',
                    color: '#00d166'
                }];
            for (m; m < rowNum; m++) {
                var clr = tdArr[m]['color'],
                    text = tdArr[m]['text'];
                var td = chartBox.append('g');
                td.append('rect')
                    .attr('x', -opt['boxMarginLeft'])
                    .attr('y', m * rowHeight + offtop)
                    .attr('width', opt['boxMarginLeft'])
                    .attr('height', rowHeight)
                    .attr('stroke', '#fff')
                    .attr('stroke-width', '1')
                    .attr('fill', clr);

                td.append('text')
                    .attr('x', -opt['boxMarginLeft'] / 2)
                    .attr('y', m * rowHeight + offtop + 12)
                    .text(text)
                    .attr('alignment-baseline', 'middle')
                    .attr('font-size', '12px')
                    .attr('fill', '#333')
                    .style('text-anchor', 'middle')
                    .style('font-weight', 'bold')
                    .style('font-family', '"Arial","Microsoft YaHei"');
            }
        };

        DChart.prototype.drawTable = function (obj, idx) {
            var opt = this.options,
                items = this.items,
                gridItems = opt.grid,
                gridItem = gridItems[idx],
                xItems = opt.xAxis,
                xItem = xItems[idx],
                xData = xItem['data'],
                xScaleLen = xData.length,
                barWidth = gridItem['width'] / xScaleLen;

            var gridGroup = items['grids'][idx],
                tableGroup = gridGroup
                    .append('g')
                    .attr('class', 'chart-table')
                    .attr('transform', 'translate(0 ' + opt['boxHeight'] + ')');

            if (!items['tableGroup']) {
                items['tableGroup'] = {};
            }
            items['tableGroup'][idx] = tableGroup; // 缓存tableGroup

            var rowHeight = 20,
                m = 0,
                rowNum = 3;
            for (m; m < rowNum; m++) {
                var n = 0, clr = '#bebebe';
                if (m == 2) {
                    clr = '#00d166';
                }
                for (n; n < xScaleLen; n++) {
                    tableGroup.append('rect')
                        .attr('x', n * barWidth)
                        .attr('y', m * rowHeight)
                        .attr('width', barWidth)
                        .attr('height', rowHeight)
                        .attr('stroke', '#fff')
                        .attr('stroke-width', '1')
                        .attr('fill', clr);
                }
            }

        };

        document.getElementById('chartView').style.marginTop = '80px';

        new DChart('#chartView', options);
    })();
</script>
</body>
</html>